home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / System 7.0 Samples / AEObject-Edition1.0.2 Sample / TextSections.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  21.4 KB  |  515 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2.  *
  3.  *  Apple Developer Technical Support
  4.  *
  5.  *  Text section handling routines
  6.  *
  7.  *  Program:    AEObject-Edition Sample
  8.  *  File:       TextSections.c -    C Source
  9.  *
  10.  *  by:         C.K. Haun <TR>
  11.  *
  12.  *  Copyright © 1990-1992 Apple Computer, Inc.
  13.  *  All rights reserved.
  14.  *
  15.  *------------------------------------------------------------------------------
  16.  * This file contains the functions for manipulating and bookkeeping
  17.  * for published and subscribed text sections in this sample.
  18.  *----------------------------------------------------------------------------*/
  19.  
  20. #define __TEXTSECTIONS__
  21.  
  22. #pragma segment TextHandler
  23. #include "Sampdefines.h"
  24.  
  25.  
  26. static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText);
  27. extern void MyHiHook(void);
  28.  
  29. Rect nulRect={0,0,0,0};
  30. /* the following few functions are for text, and will be expanded as this is fully implemented */
  31. Boolean HasTESelection(windowCHandle inWind)
  32. {
  33.     TEHandle temp;
  34.     if (inWind == nil)
  35.         return(false);
  36.     temp = (*inWind)->boxHandle;
  37.     if (temp != nil)
  38.         return((*temp)->selStart != (*temp)->selEnd);
  39.     else
  40.         return(false);
  41. }
  42.  
  43. Boolean PasteText(void)
  44. {
  45.     windowCHandle tempWC;
  46.     tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  47.     if ((*tempWC)->boxHandle != nil && gClipHasContents == kClipHasText) {
  48.         TEFromScrap();
  49.         CheckTextSections(tempWC, kAdding);
  50.         TEPaste((*tempWC)->boxHandle);
  51.         return(true);
  52.     }
  53.     return(false);
  54. }
  55.  
  56. Boolean CutText(void)
  57. {
  58.     windowCHandle tempWC;
  59.     tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  60.     if (HasTESelection(tempWC)) {
  61.         CheckTextSections(tempWC, kRemoving);               /* adjusts the end points on text sections */
  62.         TECut((*tempWC)->boxHandle);
  63.  
  64.         ZeroScrap();
  65.         TEToScrap();
  66.         return(true);
  67.     }
  68.     return(false);
  69. }
  70.  
  71. Boolean CopyText(void)
  72. {
  73.     windowCHandle tempWC;
  74.     tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  75.     if (HasTESelection(tempWC)) {
  76.         TECopy((*tempWC)->boxHandle);
  77.         ZeroScrap();
  78.         TEToScrap();
  79.         return(true);
  80.     }
  81.     return(false);
  82. }
  83.  
  84. Boolean ClearText(void)
  85. {
  86.     windowCHandle tempWC;
  87.     tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  88.     if (HasTESelection(tempWC)) {
  89.         CheckTextSections(tempWC, kRemoving);
  90.         TEDelete((*tempWC)->boxHandle);
  91.  
  92.         return(true);
  93.     }
  94.     return(false);
  95. }
  96.  
  97. /* This checks all the publishers and subscribers in the current TE record, and sees */
  98. /* if their data needs to be modified based on the TE action pending */
  99. void CheckTextSections(windowCHandle inWindow, short Action)
  100. {
  101.     TEHandle theTEHandle = (*inWindow)->boxHandle;
  102.     mySectionDataHandle startSection = (*inWindow)->textSections;
  103.     Boolean testFlag = true;                                /* a gen purp flag please */
  104.     Boolean whapBorder = false;
  105.     mySectionDataHandle currentSection = startSection;
  106.     short counter = 0;
  107.     /*  see if the insertion point or selection range is after all our sections.  */
  108.     /* if it is (a forlorn hope) we can exit without adjusting anything. */
  109.     if (startSection == nil)
  110.         return;                                             /* no sections, go away */
  111.     do {
  112.         if ((*currentSection)->endChar > (*theTEHandle)->selStart) {
  113.             testFlag = false;
  114.             counter++;                                      /* a minimal aid to exiting this stuff later */
  115.         if((*currentSection)->bordered)whapBorder=true;
  116.         }
  117.         currentSection = (*currentSection)->nextSection;
  118.     } while (currentSection);
  119.     if (testFlag)
  120.         return;                                             /* no section is affected by this action, go away */
  121.     
  122.     /* OK, someone (or two, or twenty) will be affected by this action */
  123.     /* so we have to see who, and how much, and if it's like adding in the middle, */
  124.     /* or like shifting start and end points down, or like whatever, y'know? */
  125.     
  126.     currentSection = (*inWindow)->textSections;
  127.     switch (Action) {
  128.         unsigned long actionLen;
  129.         case kKeyStroke:
  130.             /* so we bump the start and end positions of all the sections after this point by 1 */
  131.             while (counter) {                               /* minimal aid in exiting this thing */
  132.                 
  133.                 if ((*theTEHandle)->selStart <= (*currentSection)->startChar) {
  134.                     (*currentSection)->startChar++;
  135.                     (*currentSection)->endChar++;
  136.                     counter--;
  137.                 } else {
  138.                     if ((*theTEHandle)->selStart > (*currentSection)->startChar &&
  139.                         (*theTEHandle)->selStart < (*currentSection)->endChar) {
  140.                         /* this should mean that the input char is between the start and the end */
  141.                         /* so the publisher expands by 1 */
  142.                         (*currentSection)->endChar++;
  143.                         counter--;
  144.                     }
  145.                 }
  146.                 currentSection = (*currentSection)->nextSection;
  147.                 if (currentSection == nil)
  148.                     break;                                  /* hey, y'know? */
  149.             }
  150.             break;
  151.         case kAdding:
  152.             /* increase start and finished positions by the size of the clip being added */
  153.             actionLen = TEGetScrapLength();
  154.             /* so we bump the start and end positions of all the sections after this point by 1 */
  155.             while (counter) {                               /* minimal aid in exiting this thing */
  156.                 if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
  157.                     (*currentSection)->startChar += actionLen;
  158.                     (*currentSection)->endChar += actionLen;
  159.                     counter--;
  160.                 } else {Boolean temp=false;
  161.                     /* expand the published section */
  162.                   temp=(*currentSection)->bordered;
  163.                     (*currentSection)->endChar += actionLen;
  164.                     
  165.                 }
  166.                 currentSection = (*currentSection)->nextSection;
  167.                 if (currentSection == nil)
  168.                     break;                                  /* hey, y'know? */
  169.             }
  170.             break;
  171.         case kRemoving:
  172.             /* decrease start and finished positions by the size of the current selection */
  173.             /* You have another worry in this area, of course.  If the user has highlighted */
  174.             /* the WHOLE publisher or subscriber, that means that you will be deleting */
  175.             /* it completely */
  176.             actionLen = (*currentSection)->endChar - (*currentSection)->startChar;
  177.             /* so we bump the start and end positions of all the sections after this point by 1 */
  178.             while (counter) {                               /* minimal aid in exiting this thing */
  179.                 if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
  180.                     (*currentSection)->startChar -= actionLen;
  181.                     (*currentSection)->endChar -= actionLen;
  182.                     counter--;
  183.                 }
  184.                 currentSection = (*currentSection)->nextSection;
  185.                 if (currentSection == nil)
  186.                     break;                              
  187.             }
  188.             break;
  189.     }
  190.     /* if anything changed and borders are on in any section, we need to */
  191.     /* erase and redraw the borders */
  192.     /* This will cause a lot of screen flicker, you can do it neater in your app */
  193.     if(whapBorder)InvalRect(&(*theTEHandle)->viewRect);
  194. }
  195. /* KillTextBorders does about what you'd expect, it turns off all the  */
  196. /* text borders */
  197. void KillTextBorders(void)
  198. {windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  199. mySectionDataHandle startSection = (*tempWC)->textSections;
  200. while(startSection){
  201. (*startSection)->bordered = false;
  202. startSection = (*startSection)->nextSection;
  203. }
  204. }
  205.  
  206. void ShowAllTextBorders(void)
  207. {windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
  208. mySectionDataHandle startSection = (*tempWC)->textSections;
  209. while(startSection){
  210. (*startSection)->bordered = true;
  211. startSection = (*startSection)->nextSection;
  212. }
  213.  
  214.  
  215. }
  216. /* SkipOverSubscriber is used to move the insertion point around any subscriptions */
  217. /* in response to a cursor or delete key action */
  218. /* This is, of course, because you cannot edit the contents of a subscription */
  219. Boolean SkipOverSubscriber(windowCHandle inWindow, unsigned short theKey)
  220. {
  221.     long oldStart, oldEnd;
  222.     Boolean flagBack = false;
  223.     short newInsertionPoint;                                /* where the insertion point will be moving to as */
  224.     /*  a result of this key */
  225.     /* first, as usually, see if this action impinges on the target */
  226.     /* of course, this only counts for subscriptions */
  227.     TEHandle theTEHandle = (*inWindow)->boxHandle;
  228.     mySectionDataHandle startSection = (*inWindow)->textSections;
  229.     if (!startSection)
  230.         return(flagBack);                                   /* scat if no sections */
  231.     do {
  232.         /* first see if it's a publisher, if it is, skip it.  */
  233.         if (!(*startSection)->publishing) {
  234.             switch (theKey) {                               /* actions based on what direction this key is moving us */
  235.                 case kLeftArrow:
  236.                     newInsertionPoint = ((*theTEHandle)->selStart) - 1;
  237.                     if (newInsertionPoint <= (*startSection)->endChar && newInsertionPoint >= (*startSection)->startChar) {
  238.                         flagBack = true;
  239.                         TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
  240.                     }
  241.                     break;
  242.                     
  243.                 case kRightArrow:
  244.                     newInsertionPoint = ((*theTEHandle)->selEnd) + 1;
  245.                     if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
  246.                         flagBack = true;
  247.                         TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
  248.                     }
  249.                     break;
  250.                 case kUpArrow:
  251.                 case kDownArrow:
  252.                     /* for up and down arrows, we actually have to do the TEKey thing here, then evaluate the */
  253.                     /* result and see if it landed in a subscription.  This is of course because we're using */
  254.                     /* TextEdit, if you're doing your own text processing you will know where any keystroke lands */
  255.                     /* Of course, you gotta save the current position of the insertion point first */
  256.                     oldStart = (*theTEHandle)->selStart;
  257.                     oldEnd = (*theTEHandle)->selEnd;
  258.                     TEKey(theKey, theTEHandle);
  259.                     /* see where it ended up */
  260.                     newInsertionPoint = (*theTEHandle)->selEnd;
  261.                     if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
  262.                         flagBack = true;
  263.                         /* now move before or after the section depending on the arrow */
  264.                         if (theKey == kUpArrow)
  265.                             TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
  266.                         else
  267.                             TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
  268.                     } else {
  269.                         TESetSelect(oldStart, oldEnd, theTEHandle);
  270.                     }
  271.                     break;
  272.                     
  273.             }
  274.         }
  275.         startSection = (*startSection)->nextSection;        /* go to next in list */
  276.     } while (startSection);
  277.     
  278.     return(flagBack);
  279. }
  280.  
  281. /* RePackText makes sure we have the proper text in our lil publisher before writing the */
  282. /* thing out.  */
  283. /* Actually, since this function exists, we don't really need to keep a copy of the text attached */
  284. /* to the publisher.  */
  285. void RePackText(mySectionDataHandle currentSection, TEHandle theTEHandle)
  286. {
  287.     short theLength;
  288.     CharsHandle theRawText;
  289.     Ptr src;
  290.     theLength = (*currentSection)->endChar - (*currentSection)->startChar;
  291.     SetHandleSize((*currentSection)->additionalData, theLength);
  292.     HLock((*currentSection)->additionalData);
  293.     theRawText = TEGetText(theTEHandle);
  294.     HLock((Handle)theRawText);
  295.     src = (Ptr)*theRawText;
  296.     src = src + (*currentSection)->startChar;
  297.     BlockMove((Ptr)((Ptr)*theRawText)+(*currentSection)->startChar, (Ptr)*((*currentSection)->additionalData), theLength);
  298.     HUnlock((Handle)theRawText);
  299.     HUnlock((*currentSection)->additionalData);
  300. }
  301.  
  302. /* InTextBox tells us if the rectangle passed intersects the text rectangle */
  303. /* in the current window.  We use this for; */
  304. /* 1) Seeing if the mouse click was in the TERect, so we can call TEClick */
  305. /* 2) See if one of our drawing commands will overdraw the text, which is BaD */
  306. Boolean InTextBox(windowCHandle inName, Rect *where)
  307. {
  308.     Rect temp;
  309.     if ((*inName)->boxHandle == nil) {
  310.         return(false);
  311.     } else {
  312.         return(SectRect(where, &(*inName)->textBox, &temp));
  313.     }
  314. }
  315.  
  316. /* CheckSubscriberHit sees if the user person clicked inside a subscription.  If they did, then */
  317. /* we'll either highlight the whole subscriber and <sometime> draw a border around it, or */
  318. /* extend the selection range to include the whole subscription if they dragged into just part of it */
  319. /* for bording, things get a little more complicated.  If this is a publisher, and it's already */
  320. /* showing a border, we want to do a normal TEClick since the user can edit inside a publisher */
  321. void CheckSectionHit()
  322. {
  323.     windowCHandle shortName = (windowCHandle)GetWRefCon(FrontWindow());
  324.     TEHandle theTEHandle = (*shortName)->boxHandle;
  325.     long mySelStart = (*theTEHandle)->selStart;             /* added these variables just to make the code a */
  326.     long mySelEnd = (*theTEHandle)->selEnd;                 /* little easier to read */
  327.     long newStart = mySelStart;                             /* if any adjustments need to be made */
  328.     long newEnd = mySelEnd;
  329.     Boolean startedBordered;
  330.     mySectionDataHandle startSection = (*shortName)->textSections;
  331.     mySectionDataHandle currentSection = startSection;
  332.     /* and of course, the affected window is the front window, since they clicked there, y'know */
  333.     /* what we do here is see if the current mouse click lands in a publisher or */
  334.     /* subscriber.  If it's a publisher, check for a double click and highlite. */
  335.     /* if the selection range covers ANY part of a subscriber, extend the selection to */
  336.     /* encompass the whole subscriber.  Then border the subscriber also. */
  337.     /* This means that we cannot exit after finding one pub or sub, we must */
  338.     /* check all since the user could have selected all the text in the record */
  339.     while (currentSection) {
  340.         startedBordered = (*currentSection)->bordered;
  341.         if (Touching(currentSection, theTEHandle)) {
  342.             /* see if this is a subscriber.  If so, extend start and end positions to include it all */
  343.             if (!(*currentSection)->publishing) {
  344.                 /* it is a subscriber */
  345.                 if ((*currentSection)->startChar < mySelStart)
  346.                     newStart = (*currentSection)->startChar;
  347.                 if ((*currentSection)->endChar > mySelEnd)
  348.                     newEnd = (*currentSection)->endChar;
  349.         TESetSelect(newStart, newEnd, theTEHandle);        
  350.                 
  351.             } else {
  352.             /* for a publisher, highlith the whole thing if it's the first click.  If it's the */
  353.             /* second (or subsequent, o' course) they want to edit, so let them */
  354.      if (startedBordered)
  355.         TESetSelect(newStart, newEnd, theTEHandle);        
  356.     else
  357.         TESetSelect((*currentSection)->startChar, (*currentSection)->endChar, theTEHandle);
  358.             }
  359.         } else {
  360.         /* if this section was not hit, deborder it */
  361.         if(!gShowingAll && (*currentSection)->bordered){(*currentSection)->bordered=false;
  362.             InvalRect(&(*theTEHandle)->viewRect);
  363.         }}
  364.         currentSection = (*currentSection)->nextSection;
  365.     }
  366.     
  367.    
  368.     
  369. }
  370.  
  371. /*  Touching was created just so I wouldn't have to have a huge if statement in */
  372. /* CheckSectionHit */
  373. /* All it does is see if the current selection range of the TEHandle touches or */
  374. /* fully encloses any text sections */
  375. static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText)
  376. {
  377.     Boolean ouch = false;                                   /* will turn true if a touch actually happened */
  378.     
  379.     long tStart = (*teText)->selStart;
  380.     long tEnd = (*teText)->selEnd;
  381.     short ourStart = (*checkSection)->startChar;
  382.     short ourEnd = (*checkSection)->endChar;
  383.     /* first see if the start hits inside the section */
  384.     if (tStart > ourStart && tStart < ourEnd)
  385.         ouch = true;
  386.     else if (tStart < ourStart && tEnd > ourStart)
  387.         ouch = true;                                        /* now see if we got completely enclosed */
  388.     else if (tEnd > ourStart && tEnd < ourEnd)
  389.         ouch = true;
  390.     /* and finally see if we got touched by the end of the thing */
  391.     if (ouch){
  392.         BorderTextSection(checkSection);
  393.         /* put this section in our gloabl section handle holder so we can do options on it */
  394.         gShowingSecHandle=(*checkSection)->theSection;
  395.         if((*checkSection)->publishing) {
  396.         gShowPub = true;
  397.         gShowPubRect=nulRect;
  398.         } else {
  399.          gShowSub = true;
  400.         gShowSubRect=nulRect;
  401.         }}
  402.     return(ouch);
  403. }
  404.  
  405. /* ExcludeSubscriber removes subscriptions from the range of selected text, so when a keystroke */
  406. /* replaces a selection, the subscriber does not also get replaced. */
  407. void ExcludeSubscriber(windowCHandle tempCH)
  408. {
  409. #pragma unused (tempCH)    
  410. }
  411.  
  412. /* this borders the selected section.  It's a pain.  */
  413. /* BUT, it's a pain because I'm using TextEdit.  If you're writing a word processor */
  414. /* or another application which does it's own text processing, this will be a lot easier */
  415. /* •••• NOTE:  this doesn't look very good when a subscriber is embedded in a publisher, since */
  416. /* the lines of course overlap.  I don't know how to deal with this yet. */
  417. void BorderTextSection(mySectionDataHandle theText)
  418. {
  419.     TEHandle theTEHandle;
  420.     WindowPtr theWindow;
  421.     windowCHandle shortName;
  422.     PolyHandle borderPoly;
  423.     long origIStart;
  424.     long origIEnd;
  425.     long newStart;
  426.     long newEnd;
  427.     Point startPoint;
  428.     Point endPoint;
  429.     RgnHandle oldClip = NewRgn();
  430.     WindowPtr oldPort;
  431.     GetPort(&oldPort);
  432.     GetClip(oldClip);
  433.     
  434.     theWindow = FindSection((*theText)->theSection);
  435.     SetPort(theWindow);
  436.     shortName = (windowCHandle)GetWRefCon(theWindow);
  437.     HLock((Handle)shortName);
  438.     theTEHandle = (*shortName)->boxHandle;
  439.     
  440.     origIStart = (*theTEHandle)->selStart;
  441.     origIEnd = (*theTEHandle)->selEnd;
  442.     TESetSelect((*theText)->startChar, (*theText)->endChar, theTEHandle);
  443.     newStart = (*theTEHandle)->selStart;
  444.     newEnd = (*theTEHandle)->selEnd;
  445.     ClipRect(&(*theTEHandle)->viewRect);
  446.     /* so our poly doesn't exceed the bounds of our rect */
  447.     /* now what we do is create a Polygon that's the shape of the text section */
  448.     startPoint = TEGetPoint((short)newStart, theTEHandle);
  449.     endPoint = TEGetPoint((short)newEnd, theTEHandle);
  450.     /* •••• NOTE:  This is a really simplistic check, since this is a simple sample.  */
  451.     /* In a more complex text document you'd be worrying about text scrolled out of the view rect, */
  452.     /* weird regions, and other things I'm not going to deal with here.  */
  453.     /* see if the verts are the same.  If they are, then all we need is a simple rect */
  454.     
  455.     if (startPoint.v == endPoint.v) {
  456.         borderPoly = OpenPoly();
  457.         startPoint.v -= (*theTEHandle)->lineHeight;         /* top o' the line to you! */
  458.         
  459.         MoveTo(startPoint.h, startPoint.v);
  460.         LineTo(endPoint.h, startPoint.v);
  461.         LineTo(endPoint.h, endPoint.v);
  462.         LineTo(startPoint.h, endPoint.v);
  463.         LineTo(startPoint.h, startPoint.v);
  464.         ClosePoly();
  465.         PenSize(3, 3);
  466.         if ((*theText)->publishing)
  467.             PenPat(&qd.gray);
  468.         else
  469.             PenPat(&qd.dkGray);
  470.         FramePoly(borderPoly);
  471.         PenPat(&qd.black);
  472.         PenSize(1, 1);
  473.         
  474.     } else {
  475.         short endTop = endPoint.v -(*theTEHandle)->lineHeight;
  476.         /* need to build a slightly more complex poly */
  477.         borderPoly = OpenPoly();
  478.         startPoint.v -= (*theTEHandle)->lineHeight;         /* top o' the line to you! */
  479.         
  480.         MoveTo(startPoint.h, startPoint.v);
  481.         LineTo((*theTEHandle)->destRect.right, startPoint.v);
  482.         LineTo((*theTEHandle)->destRect.right, endTop);
  483.         LineTo(endPoint.h, endTop);                         /* may not be needed, but doesn't hurt anything */
  484.         LineTo(endPoint.h, endPoint.v);
  485.         LineTo((*theTEHandle)->destRect.left, endPoint.v);
  486.         LineTo((*theTEHandle)->destRect.left, (startPoint.v + ((*theTEHandle)->lineHeight)));
  487.         LineTo(startPoint.h, (startPoint.v + ((*theTEHandle)->lineHeight)));
  488.         LineTo(startPoint.h, startPoint.v);
  489.         ClosePoly();
  490.         PenSize(3, 3);
  491.         if ((*theText)->publishing)
  492.             PenPat(&qd.gray);
  493.         else
  494.             PenPat(&qd.dkGray);
  495.         FramePoly(borderPoly);
  496.         PenPat(&qd.black);
  497.         PenSize(1, 1);
  498.         
  499.     }
  500.     if((*theText)->bordered)
  501.     /* if it started bordered, and it's a publisher, then when set the selection range to */
  502.     /* what it was when we entered */
  503.         TESetSelect(origIStart,origIEnd, theTEHandle);    
  504.      else 
  505.         (*theText)->bordered = true;     /* log it bordered */
  506.     KillPoly(borderPoly);
  507.     SetClip(oldClip);
  508.     DisposeRgn(oldClip);
  509.     SetPort(oldPort);
  510.     HUnlock((Handle)shortName);
  511. }
  512.  
  513.  
  514. #undef __TEXTSECTIONS__
  515.